#region Imported Namespaces


using System;
using System.Collections.ObjectModel;


#endregion


namespace WtfManager.Framework
{
  ///<summary>
  /// Base class for extensible framework components.
  ///</summary>
  public abstract class ExtensibleService: Service
  {
    #region Fields


    private readonly ObservableCollection<Service> _extensions = new ObservableCollection<Service>();
    private readonly ObservableCollection<Service> _initializedExtensions = new ObservableCollection<Service>();


    #endregion


    #region Properties/Indexers


    ///<summary>
    /// Gets a list of extensions, that are assigned to this service.
    ///</summary>
    public ReadOnlyObservableCollection<Service> Extensions
    {
      get
      {
        return new ReadOnlyObservableCollection<Service>(_initializedExtensions);
      }
    }


    #endregion


    #region Methods


    /// <exception cref="InvalidOperationException">Specified extension was already added or not valid.</exception>
    internal void AddExtension(Service extension)
    {
      if (_extensions.Contains(extension))
      {
        throw new InvalidOperationException("Specified extension was already added.");
      }

      _extensions.Add(extension);
      extension.StateChanged += ExtensionStateChanged;

      if (extension.State != FrameworkComponentState.Initialized)
      {
        return;
      }

      AddExtensionToList(extension);
    }


    private void AddExtensionToList(Service extension)
    {
      if (_initializedExtensions.Contains(extension))
      {
        throw new InvalidOperationException();
      }

      _initializedExtensions.Add(extension);

      OnExtensionAdded(extension);
    }


    internal void RemoveExtension(Service extension)
    {
      var removed = _extensions.Remove(extension);

      if(!removed)
      {
        throw new InvalidOperationException();
      }

      extension.StateChanged -= ExtensionStateChanged;

      RemoveExtensionFromList(extension);
    }


    private void RemoveExtensionFromList(Service extension)
    {
      bool removed = _initializedExtensions.Remove(extension);

      if (removed)
      {
        OnExtensionRemoved(extension);
      }
    }


    private void ExtensionStateChanged(object sender, EventArgs e)
    {
      var extension = (Service)sender;

      if (extension.State == FrameworkComponentState.Initialized)
      {
        AddExtensionToList(extension);
      }
      else
      {
        RemoveExtensionFromList(extension);
      }

      if (_extensions.Count == _initializedExtensions.Count)
      {
        State = FrameworkComponentState.Initialized;
      }
    }


    internal override FrameworkComponentState InternalOnInitialize()
    {
      if (_extensions.Count == _initializedExtensions.Count)
      {
        return FrameworkComponentState.Initialized;
      }

      return FrameworkComponentState.Initializing;
    }


    ///<summary>
    /// Called when an extension was removed.
    ///</summary>
    protected abstract void OnExtensionRemoved(Service extension);


    ///<summary>
    /// Called when extension was added.
    ///</summary>
    protected abstract void OnExtensionAdded(Service extension);


    ///<summary>
    /// Determines whether a specified service is an extension for current service.
    ///</summary>
    ///<param name="service">A service to check.</param>
    ///<returns>True, if a specified service is an extension for current service, otherwise false.</returns>
    public abstract bool IsExtension(Service service);


    internal bool Contains(Service extension)
    {
      return _extensions.Contains(extension) || _initializedExtensions.Contains(extension);
    }


    #endregion
  }
}